home *** CD-ROM | disk | FTP | other *** search
- /* $Header: /usr/people/pcd/Src/RTF/RCS/view.C,v 1.1 92/11/23 12:58:56 pcd Exp Locker: pcd $
- */
-
- #include "view.h"
-
- #include <assert.h>
- #include <string.h>
- #include "debug.h"
-
- int debug_rtf = 0;
- int debug_rtfall = 0;
-
- class RTFParagraphFormat : public ParagraphFormat{
- public:
- void set_attr(int, int);
-
- Inches li(int twips)
- { return left_indent = (double)twips / 1440; };
-
- Inches ri(int twips)
- { return right_indent = (double)twips / 1440; };
-
- Inches fi(int twips)
- { return first_indent = (double)twips / 1440; };
-
- Inches sb(int twips)
- { return space_before = (double)twips / 1440; };
-
- Inches sa(int twips)
- { return space_after = (double)twips / 1440; };
-
- Inches sl(int twips)
- { return space_between = (double)twips / 1440; };
-
- static void deftab(int twips)
- { default_tab_width((double)twips / 1440); };
-
- void tx(int twips)
- { add_tab_stop((double)twips / 1440 + left_indent); };
-
- };
-
-
- void
- ParagraphFormat::normal()
- {
- first_indent = left_indent = right_indent = 0;
- space_before = space_after = space_between = 0;
- tabqty = 0;
- justification = quad_left;
- }
-
- void
- RTFParagraphFormat::set_attr(int attr, int val)
- {
- switch(attr){
- case RTF::ParDef:
- normal();
- break;
-
- case RTF::QuadLeft:
- justification = quad_left;
- break;
-
- case RTF::QuadRight:
- justification = quad_right;
- break;
-
- case RTF::QuadJust:
- justification = quad_full;
- break;
-
- case RTF::QuadCenter:
- justification = quad_center;
- break;
-
- case RTF::CellPos: /* @@ table hack */
- case RTF::TabPos:
- tx(val);
- break;
-
- case RTF::SpaceBefore:
- sb(val);
- break;
-
- case RTF::SpaceAfter:
- sa(val);
- break;
-
- case RTF::SpaceBetween:
- sl(val);
- break;
-
- case RTF::LeftIndent:
- li(val);
- break;
-
- case RTF::RightIndent:
- ri(val);
- break;
-
- case RTF::FirstIndent:
- fi(val);
- break;
-
- //@@ others!
- }
- }
-
- void
- RTFCharacterFormat::set_attr(int attr, int val)
- {
- switch(attr){
- case RTF::Plain:
- plain();
- break;
-
- case RTF::Bold:
- bold = val;
- break;
-
- case RTF::Italic:
- italic = val;
- break;
-
- case RTF::Underline:
- /* Microsoft RTF never uses \ul0
- but I'm supporting it because NeXT rtf files do.
- */
- underline = val;
- break;
-
- case RTF::NoUnderline:
- underline = 0;
- break;
-
- case RTF::FontFamily:
- font_family = val;
- break;
-
- case RTF::SuperScript:
- super = val;
- break;
-
- case RTF::SubScript:
- sub = val;
- break;
-
- #ifdef FONT_SIZES
- case RTF::FontSize:
- size = val;
- break;
- #endif
-
- case RTF::ForeColor:
- foreground = val;
- break;
- }
- }
-
- View::View()
- {
- flow_ = 0;
- page_ = 0;
- first_ = last_ = 0;
- }
-
- View::~View()
- {
- delete flow_;
- delete page_;
- }
-
-
- int
- View::string(const char* d, int rtf)
- {
- delete page_;
- page_ = 0;
- delete flow_;
- flow_ = 0;
-
- Qty q;
- if(d == 0 || (q = strlen(d)) == 0)
- return 1;
-
- if(rtf)
- flow_ = RichText(d, q);
- else
- flow_ = ASCII(d, q);
-
- first_ = 0;
- last_ = flow_ ? flow_->bytes() : 0;
- clear();
-
- return flow_ ? 1 : 0;
- }
-
-
- int
- View::format()
- {
- if(!flow_) return 0; /* must be text available */
-
- if(page_) /* already formatted */
- return 1;
-
- page_ = new TextRect();
-
- page_->format(flow_, first(), last(), bounds_);
- /* page_->bounds().top() is end of formatted area */
- bounds_.height(page_->bounds().top() - bounds_.top());
- return 1;
- }
-
- int
- View::resize(BRect b)
- {
- if(bounds_ == b)
- return 0;
-
- bounds_ = b;
-
- unformat();
- return 1;
- }
-
- void
- View::unformat()
- {
- if(page_){
- delete page_;
- page_ = 0;
-
- clear();
- }
- }
-
-
- int
- View::range(TextPosition f, TextPosition l)
- {
- if(f == first() && l == last())
- return 0;
-
- first_ = f;
- last_ = l;
-
- unformat();
- return 1;
- }
-
-
- TextFlow*
- View::ASCII(const char* data, Qty q)
- {
- TextFlow* ret = new TextFlow((const Byte*)data, q);
-
- TextPosition first, last;
- CharacterFormat plain;
- plain.fg_pixel = color(0,0,0); //@@ should be in CharacterFormat::plain()
-
- char* p;
-
- for(first = 0, p=(char*)data;
- first < q && (p = strchr(p, '\n'));
- p++, first=last+1){
- last = p - (char*)data;
- if(last>first)
- text_flow(*ret, first, last, plain);
- new_line(*ret, last, last+1, 0);
- }
-
- if(first < q)
- text_flow(*ret, first, q, plain);
-
- return ret;
- }
-
-
- class IntTable {
- public:
- IntTable(IntTable* s, int indx, int elt)
- { link_ = s; indx_ = indx; elt_ = elt; };
-
- ~IntTable()
- { delete link_; }
-
- int element(int indx)
- // return 0 on not found
- { return indx_ == indx ? elt_ : link_ ? link_->element(indx) : 0;};
-
- private:
- int elt_, indx_;
- IntTable* link_;
- };
-
- class ColorSequence {
- public:
- ColorSequence(ColorSequence* s, unsigned long p)
- { link_ = s; pixel_ = p; };
-
- ~ColorSequence()
- { delete link_; }
-
- ColorSequence* reverse(ColorSequence* prev=0)
- { ColorSequence* l = link_;
- link_ = prev;
- return l ? l->reverse(this) : this;
- };
-
- unsigned long element(int indx)
- // return 0 on not found
- { return indx == 0 ? pixel_
- : link_ ? link_->element(indx-1) : 0; };
-
- private:
- unsigned long pixel_;
- ColorSequence* link_;
- };
-
-
- static IntTable*
- parse_font_table(RTFPos& here)
- {
- IntTable* ret = 0;
- int fnum = 0; // appease compiler.
-
- int braces = 1;
- do{
- here.GetToken();
- if(here.CheckCM(RTF::Control, RTF::FontFamily))
- ret = new IntTable(ret, fnum, here.Minor() - RTF::FFNil);
- else if(here.CheckCMM(RTF::Control, RTF::CharAttr, RTF::FontNum))
- fnum = here.Param();
- else if(here.CheckCM(RTF::Group, RTF::BeginGroup))
- braces++;
- else if(here.CheckCM(RTF::Group, RTF::EndGroup))
- braces--;
- }while(braces>0);
- return ret;
- }
-
- static ColorSequence*
- parse_color_table(RTFPos& here, const View& view)
- {
- ColorSequence* ret = 0;
- unsigned char r, g, b;
-
- int braces = 1;
- do{
- here.GetToken();
- if(here.CheckCM(RTF::Control, RTF::ColorName))
- switch(here.Minor()){
- case RTF::Red:
- r = here.Param();
- break;
-
- case RTF::Green:
- g = here.Param();
- break;
-
- case RTF::Blue:
- b = here.Param();
- ret = new ColorSequence(ret, view.color(r<<8, g<<8, b<<8));
- break;
-
- }
- else if(here.CheckCM(RTF::Group, RTF::BeginGroup))
- braces++;
- else if(here.CheckCM(RTF::Group, RTF::EndGroup))
- braces--;
- }while(braces>0);
- return ret ? ret->reverse() : 0;
- }
-
- TextFlow*
- View::RichText(const char* data, Qty q)
- {
- assert(data && q>0);
-
- RTFPos token(data);
-
- token.GetToken();
- REQUIRE(token.CheckCM(RTF::Group, RTF::BeginGroup), return 0);
-
- TextFlow* parent_group = new TextFlow((const Byte*)data, q);
-
- RTFParagraphFormat::deftab(720);
- RTFParagraphFormat pgf_fmt;
- RTFCharacterFormat* char_fmt = new RTFCharacterFormat(0);
- IntTable* font_table = 0;
- ColorSequence* color_table = 0;
-
- TextPosition start_paragraph=0;
-
- int braces = 1;
-
- token.GetToken();
-
- while(braces > 0 && token.start() < q){
- Debug(rtfall, ("RTF parse token: '%s'\n", token.TokenText()));
- switch(token.Class()){
- case RTF::Group:
- if(token.Major() == RTF::BeginGroup){
- char_fmt = new RTFCharacterFormat(char_fmt);
- braces++;
- }else{
- char_fmt = char_fmt->pop();
- braces--;
- }
- break;
-
- case RTF::Text:
- //@@ fg_pixel should be maintained by RTFCharacterFormat
- char_fmt->fg_pixel = color_table ?
- color_table->element(char_fmt->foreground)
- : color(0,0,0);
-
- if(token.Minor()){
- text_char(*parent_group, token.start(), token.end(), token.Major(),
- *char_fmt);
- }else{
- parse_text(token, *parent_group, *char_fmt);
- continue;
- }
- break;
-
- case RTF::Control:
- switch(token.Major()){
-
- case RTF::DocAttr:
- switch(token.Minor()){
- case RTF::DefTab:
- RTFParagraphFormat::deftab(token.Param());
- break;
- }
- break;
-
- case RTF::CharAttr:
- if(token.Minor() == RTF::Invisible)
- token.SkipGroup();
- else if(token.Minor() == RTF::FontNum){
- if(font_table)
- // element returns 0, i.e. fnil on errors
- char_fmt->set_attr(RTF::FontFamily,
- font_table->element(token.Param()));
- }
- else
- char_fmt->set_attr(token.Minor(), token.Param());
- break;
-
- case RTF::ParAttr:
- pgf_fmt.set_attr(token.Minor(), token.Param());
- break;
-
- case RTF::SpecialChar:
- switch(token.Minor()){
- extern int debug_tabs;
- case RTF::Tab:
- case RTF::Cell: /* @@ hack to make sense of tables */
- new TabFlow(*parent_group, token.start(), token.end(),
- pgf_fmt, *this);
- break;
-
- case RTF::Line:
- case RTF::Row: /* @@ hack to make sense of tables */
- new_line(*parent_group, token.start(), token.end(), 0);
- break;
-
- case RTF::Page: //@#
- case RTF::Par:
- //@@ use char format for height of newline
- new_line(*parent_group, token.start(), token.end(),
- pgf_fmt.space_after);
- new ParagraphFlow(*parent_group, start_paragraph, token.end(),
- pgf_fmt, *this);
- start_paragraph = token.end();
- break;
- }
- break;
-
- case RTF::Destination:
- switch(token.Minor()){
- case RTF::Field:
- case RTF::FieldResult:
- case RTF::Index:
- break;
-
- case RTF::FontTbl:
- font_table = parse_font_table(token);
- char_fmt = char_fmt->pop();
- braces--;
- break;
-
- case RTF::ColorTbl:
- color_table = parse_color_table(token, *this);
- char_fmt = char_fmt->pop();
- braces--;
- break;
-
- case RTF::Pict:
- parse_picture(token, *parent_group);
- /* and skipgroup... */
- default:
- token.SkipGroup();
- char_fmt = char_fmt->pop();
- braces--;
- break;
- }
- }
- }
- token.GetToken();
- }
- return parent_group;
- }
-
-
- void
- RTFPos::getText()
- {
- size_t len = strcspn(p, "\t\n\r\\{}");
- end_+= len;
- p+= len;
- }
-
-
- void
- RTFPos::getHex()
- {
- size_t len = strcspn(p, "\\{}");
- end_+= len;
- p+= len;
- }
-
-
- void
- View::parse_text(RTFPos& token, TextFlow& parent, const CharacterFormat& cf)
- {
- TextPosition start = token.start();
-
- token.getText();
- Debug(rtf, ("word(%d,%d)\n", start, token.start()));
- text_flow(parent, start, token.end(), cf);
- token.GetToken();
- }
-
-
- void
- View::parse_picture(RTFPos& token, TextFlow& parent_group)
- {
- TextPosition start = -1;
- Coord height=-1, width=-1;
-
- do{
- if(token.CheckCM(RTF::Control, RTF::PictAttr)){
- switch(token.Minor()){
- case RTF::PicWid:
- width = token.Param();
- break;
- case RTF::PicHt:
- height = token.Param();
- break;
- /* @# others? bitmap type? */
- }
- }else
- if(token.Class() == RTF::Text){
- start = token.start();
- token.getHex();
- }
- token.GetToken();
- }while(token.Class() != RTF::Group && token.Class() != RTF::Eof);
-
- if(height > 0 && width > 0 && token.start() > start)
- /* all color info must be in the hex data.
- * right now, SunRaster/RLE (1 or 8 bit) is used
- * @@ pass a bitmap type?
- */
- picture(parent_group, start, token.start(), width, height);
- }
-
-
- char*
- View::plain_text(const char* rich_text, Qty q)
- {
- RTFPos token(rich_text);
- char *ret = new char[q+1];
- char *dest = ret;
-
- token.GetToken();
-
- while(token.start() < q){
- switch(token.Class()){
- case RTF::Group:
- break;
-
- case RTF::Text:
- *dest++ = token.Major();
- break;
-
- case RTF::Control:
- switch(token.Major()){
-
- case RTF::CharAttr:
- if(token.Minor() == RTF::Invisible)
- token.SkipGroup();
- break;
-
- case RTF::ParAttr:
- break;
-
- case RTF::SpecialChar:
- switch(token.Minor()){
- case RTF::Tab:
- *dest++ = '\t';
- break;
-
- case RTF::Line:
- *dest++ = '\n';
- break;
-
- case RTF::Page:
- *dest++ = '\f';
- break;
-
- case RTF::Par:
- *dest++ = '\n';
- *dest++ = '\n';
- break;
- }
- break;
-
- case RTF::Destination:
- switch(token.Minor()){
- case RTF::Field:
- case RTF::FieldResult:
- case RTF::Index:
- break;
-
- default:
- token.SkipGroup();
- break;
- }
- }
- }
- token.GetToken();
- }
- *dest++ = 0;
- return ret;
- }
-